home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / System / SmartDragWindow 1.0.1 / SmartDragWindow.c < prev    next >
Text File  |  1995-11-22  |  24KB  |  717 lines

  1. ragResultRect,
  2.     SnapCallback snapProc);
  3.  
  4. static void AdjustWindowForStructure(WindowRef theWindow, Rect *adjustRect);
  5.  
  6. static void SnapBottomToBottom(Rect *snapRect, const Rect *magnetRect, short snapToDistance);
  7. static void SnapTopToTop(Rect *snapRect, const Rect *magnetRect, short snapToDistance);
  8. static void SnapRightToRight(Rect *snapRect, const Rect *magnetRect, short snapToDistance);
  9. static void SnapLeftToLeft(Rect *snapRect, const Rect *magnetRect, short snapToDistance);
  10.  
  11. static Boolean WithinRange(short theValue, short rangeValue, short range);
  12. static void MoveRectTo(Rect *theRect, short h, short v);
  13.  
  14. static GDHandle GetDominantDevice (Rect *r);
  15. static Boolean IsActiveScreenDevice();
  16. static long GetRectArea(Rect r);
  17.  
  18. // ---------------------------------------------------------------------------
  19.  
  20. #pragma mark === Hiep Dam ===
  21.  
  22.  
  23. enum {
  24.     kSnapToMargin = 10
  25. };
  26.  
  27. #define kExtremeNeg        -32768
  28. #define kExtremePos        (32767 - 1) // required to address an old region bug,
  29.                                     //see develop 20 Q&As
  30.  
  31. #define HOTKEY_DISABLE    IsControlKeyDown()
  32.  
  33. // ---------------------------------------------------------------------------
  34.  
  35. void SmartDragWindow(
  36.     WindowRef windowToDrag,
  37.     Point startPoint,
  38.     const Rect *limitRect,
  39.     short snapToDistance) {
  40.     
  41.     SuperSmartDragWindow(windowToDrag, startPoint, limitRect, snapToDistance, MonitorSnapProc);
  42. } // END SmartDragWindow
  43.  
  44. // ---------------------------------------------------------------------------
  45.  
  46. void SuperSmartDragWindow(
  47.     WindowRef windowToDrag,
  48.     Point startPoint,
  49.     const Rect *limitRect,
  50.     short snapToDistance,
  51.     SnapCallback snapProc) {
  52.  
  53.     GrafPtr        wMgrPort;
  54.     RgnHandle    wMgrSaveRgn;
  55.     RgnHandle    clipRgn;
  56.     RgnHandle    strucRgn;
  57.     PenState    wMgrPenState;
  58.     Rect        dragResult;
  59.     Rect        menuBarRect;
  60.     Boolean        selectWindow;
  61.     Boolean        dragOK;
  62.     WindowPtr    aWindow;
  63.     GrafPtr        savePort;
  64.     Rect        wideOpen;
  65.  
  66.     if (HOTKEY_DISABLE)
  67.         snapProc = NULL;
  68.  
  69.     // Is this a normal drag or a "cmd-key" drag?
  70.     selectWindow = true;
  71.     if (windowToDrag != FrontWindow() && IsCmdKeyDown())
  72.         selectWindow = false;
  73.  
  74.     GetPort(&savePort);
  75.     GetWMgrPort(&wMgrPort);
  76.     
  77.     // Set to window manager port & get its settings (pen, clipping region)
  78.     SetPort(wMgrPort);
  79.     GetPenState(&wMgrPenState);
  80.     wMgrSaveRgn = NewRgn();
  81.     GetClip(wMgrSaveRgn);
  82.  
  83.     // Enlarge clipping area so we can drag all over the place...
  84.     SetRect(&wideOpen, kExtremeNeg, kExtremeNeg, kExtremePos, kExtremePos);
  85.     ClipRect(&wideOpen);
  86.     clipRgn = NewRgn();
  87.     GetClip(clipRgn);
  88.     
  89.     strucRgn = NewRgn();
  90.     // Account for any windows which may be on top of ours. If there
  91.     // are any, subtract their structure regions from the clipping
  92.     // region so we don't draw into them (just like what DragWindow() does).
  93.     if (windowToDrag != FrontWindow()) {
  94.         // Get first window
  95.         aWindow = FrontWindow();
  96.  
  97.         while (aWindow != windowToDrag && aWindow != NULL) {
  98.             if (IsWindowVisible(aWindow)) {
  99.                 GetWindowStructureRgn(aWindow, strucRgn);
  100.                 DiffRgn(clipRgn, strucRgn, clipRgn);
  101.             }
  102.             aWindow = GetNextWindow(aWindow);
  103.         }
  104.     }
  105.  
  106.     // Remember to exclude menu bar as well. In this case we treat
  107.     // it like one of the windows above - we subtract it's area
  108.     // from the total draggable clipping region.
  109.     // This part unfortunately relies on a low-memory global...
  110.     menuBarRect = (**GetMainDevice()).gdRect;
  111.     menuBarRect.bottom = menuBarRect.top + LMGetMBarHeight();
  112.     RectRgn(strucRgn, &menuBarRect);
  113.     DiffRgn(clipRgn, strucRgn, clipRgn);
  114.     DisposeRgn(strucRgn);
  115.  
  116.     // Set our "adjusted" clipping region
  117.     SetClip(clipRgn);
  118.  
  119.     // Illegal snap-to distance?
  120.     if (snapToDistance < 0)
  121.         snapToDistance = kSnapToMargin;
  122.  
  123.     // Do the actual dirty work
  124.     dragOK = DoWindowDrag(
  125.         windowToDrag,
  126.         startPoint,
  127.         limitRect,
  128.         snapToDistance,
  129.         &dragResult,
  130.         snapProc);
  131.     
  132.     // Restore window manager port
  133.     SetClip(wMgrSaveRgn);
  134.     SetPenState(&wMgrPenState);
  135.     DisposeRgn(wMgrSaveRgn);
  136.     DisposeRgn(clipRgn);
  137.  
  138.     if (dragOK) {
  139.         // Adjust dragResult for structure region of window (i.e. drag bar)
  140.         AdjustWindowForStructure(windowToDrag, &dragResult);
  141.         MoveWindow(windowToDrag, dragResult.left, dragResult.top, selectWindow);
  142.     }
  143.  
  144.     SetPort(savePort);
  145. } // END SmartDragWindow
  146.  
  147. // ---------------------------------------------------------------------------
  148.  
  149. /*
  150.     DoWindowDrag().
  151.     The real meat 'n potatoes of the outfit.
  152.     
  153.     Returns true if mouseloc was inside limitRect when mouse was released,
  154.     else false.
  155.     If true, argument <dragResultRect> will contain the new dragged rect
  156.     (note: the rect is the size of the window's structure region and
  157.     needs to be adjusted before calling MoveWindow())
  158. */
  159.  
  160. Boolean DoWindowDrag(
  161.     WindowPtr    dragWindow,
  162.     Point        startPoint,
  163.     const Rect    *limitRect,
  164.     short        snapToDistance,
  165.     Rect        *dragResultRect,
  166.     SnapCallback snapProc) {
  167.  
  168.     Rect        dragRect;
  169.     Rect        oldRect;
  170.     Rect        confineRect;
  171.     Rect        grayRgnRect;
  172.     RgnHandle    strucRgn;
  173.     Point        mouseLoc;
  174.     short        offsetH,
  175.                 offsetV;
  176.     short        windowWidth,
  177.                 windowHeight;
  178.     Boolean        insideConfineRect;
  179.     char        i;
  180.     Pattern        grayPat;
  181.     Pattern        blackPat;
  182.  
  183.     /*
  184.         Why can't we auto-initialize the patterns grayPat & blackPat,
  185.         as in "grayPat = { 0xAA, 0x55, 0xAA, 0x55, etc... }"?
  186.  
  187.         Well, looking at the definition of a Pattern, it's a string
  188.         of 8 chars! So the above is a string initialization, which in
  189.         turn makes the initialization string 0xAA, 0x55 a global
  190.         string. This is NOT what is intended! Doing this will cause
  191.         the compiler to allocate at least 16 bytes of global data.
  192.     */
  193.     for (i = 0; i < 8; i++) {
  194.         grayPat.pat[i] = 0xAA;
  195.         blackPat.pat[i] = 0xAA;
  196.     }
  197.     for (i = 1; i < 8; i += 2)
  198.         grayPat.pat[i] = 0x55;
  199.  
  200.     // Determine limit rect (& be smart about it!)
  201.     grayRgnRect = (**GetGrayRgn()).rgnBBox;
  202.     if (limitRect == NULL || EmptyRect(limitRect)) {
  203.         // Don't use screenBits.bounds, please!
  204.         confineRect = grayRgnRect;
  205.         InsetRect(&confineRect, 4, 4);
  206.     }
  207.     else {
  208.         // If users passed the grayRgn or screenBits.bounds, adjust
  209.         // the limit rect for them, just like DragWindow() does.
  210.         // We shouldn't use globals, so assuming that screenBits.bounds
  211.         // is the same as (**GetMainDevice).gdRect...
  212.         if (EqualRect(limitRect, &grayRgnRect) ||
  213.             EqualRect(limitRect, &(**GetMainDevice()).gdRect)) {
  214.             confineRect = grayRgnRect;
  215.             InsetRect(&confineRect, 4, 4);
  216.         }
  217.         else {
  218.             // Passed a non-standard limit rect. Use it instead.
  219.             confineRect = *limitRect;
  220.         }
  221.     }
  222.     insideConfineRect = true;
  223.     
  224.     // Get window height, width
  225.     strucRgn = NewRgn();
  226.     GetWindowStructureRgn(dragWindow, strucRgn);
  227.     windowWidth = dragRect.right - dragRect.left;
  228.     windowHeight = dragRect.bottom - dragRect.top;
  229.     dragRect = (**strucRgn).rgnBBox;
  230.  
  231.     // Since the mouse probably isn't at the topleft of the window,
  232.     // we have to make adjustments
  233.     offsetH = startPoint.h - dragRect.left;
  234.     offsetV = startPoint.v - dragRect.top;
  235.  
  236.     // Now we can rubberband
  237.     PenMode(srcXor);
  238.     PenPat(&grayPat);
  239.     // Draw it for the first time
  240.     oldRect = dragRect;
  241.     FrameRect(&dragRect);
  242.  
  243.     while (StillDown()) {        
  244.         GetMouse(&mouseLoc);
  245.  
  246.         if (PtInRect(mouseLoc, &confineRect)) {
  247.             MoveRectTo(&dragRect, mouseLoc.h - offsetH, mouseLoc.v - offsetV);
  248.             
  249.             if (snapProc)
  250.                 snapProc(dragWindow, snapToDistance, &dragRect);
  251.  
  252.             // Draw only if mouse moved and it moved inside of limit rect
  253.             if (!insideConfineRect) {
  254.                 // Mouse was outside of limit rect and now it
  255.                 // has moved back inside.
  256.                 FrameRect(&dragRect);
  257.                 oldRect = dragRect;
  258.             }
  259.             else if (!EqualRect(&oldRect, &dragRect)) {
  260.                 FrameRect(&dragRect);
  261.                 FrameRect(&oldRect);
  262.                 oldRect = dragRect;
  263.             }
  264.  
  265.             insideConfineRect = true;
  266.         }
  267.         else {
  268.             if (insideConfineRect) {
  269.                 // Mouse was inside limit rect and now has moved
  270.                 // outside of it.
  271.  
  272.                 // Erase drag outline
  273.                 FrameRect(&oldRect);
  274.                 insideConfineRect = false;
  275.             }
  276.         }
  277.     }
  278.     
  279.     // Erase the last drag outline (if it hasn't been
  280.     // already erased by the mouse being outside of confineRect)
  281.     if (insideConfineRect)
  282.         FrameRect(&dragRect);
  283.  
  284.     DisposeRgn(strucRgn);
  285.  
  286.     // Restore drawing modes
  287.     PenMode(srcCopy);
  288.     PenPat(&blackPat);
  289.     
  290.     if (insideConfineRect)
  291.         *dragResultRect = dragRect;
  292.  
  293.     return(insideConfineRect);
  294. } // END DoWindowDrag
  295.  
  296. // ---------------------------------------------------------------------------
  297.  
  298. /*
  299.     Adjust the drag rect by accounting for the window's drag bar.
  300.     DoWindowDrag() returns the dragged rect - but this rect is the
  301.     total structure rect of the window. MoveWindow(), on the other
  302.     hand, expects the rect to be in terms of the content region.
  303.     So we have to adjust it...
  304. */
  305.  
  306. void AdjustWindowForStructure(WindowRef theWindow, Rect *adjustRect) {
  307.     RgnHandle strucRgn, contRgn;
  308.     
  309.     strucRgn = NewRgn();
  310.     contRgn = NewRgn();
  311.     
  312.     GetWindowStructureRgn(theWindow, strucRgn);
  313.     GetWindowContentRgn(theWindow, contRgn);
  314.     
  315.     // We only have to worry about the left and top sides
  316.     // of the rect, since MoveWindow takes only h and v arguments.
  317.     adjustRect->top += (**contRgn).rgnBBox.top - (**strucRgn).rgnBBox.top;
  318.     adjustRect->left += (**contRgn).rgnBBox.left - (**strucRgn).rgnBBox.left;
  319.     
  320.     // Here is a tricky situation. If the window is "rolled up", a la
  321.     // Aaron, we get errorneous contRgn areas. The top
  322.     // of the contRgn is one less than it is if it wasn't rolled up.
  323.     // So we have to adjust for this.
  324.     // WindowShade does use the correct values.
  325.     /*
  326.     if ((**contRgn).rgnBBox.top == (**contRgn).rgnBBox.bottom) {
  327.         adjustRect->top++;
  328.     }
  329.     */
  330.  
  331.     DisposeRgn(strucRgn);
  332.     DisposeRgn(contRgn);
  333. } // END AdjustWindowForStructure
  334.  
  335. // ---------------------------------------------------------------------------
  336.  
  337. /*
  338.     MonitorSnapProc.
  339.     Snaps the rect to the edges of the monitor. Multiple-monitor savvy.
  340. */
  341.  
  342. void MonitorSnapProc(WindowPtr windowToDrag, short snapToDistance, Rect *snapRect) {
  343.     GDHandle    theMonitor;
  344.     Rect        monitorRect;
  345.  
  346.     // This is where we adjust the drag rect to "snap to" the
  347.     // edges of the monitors.
  348.     theMonitor = GetDominantDevice(snapRect);
  349.     monitorRect = (**theMonitor).gdRect;
  350.     if (theMonitor == GetMainDevice()) {
  351.         // If this is the main monitor, adjust "snap to" rect
  352.         // to account for menu bar.
  353.         monitorRect.top += LMGetMBarHeight();
  354.     }
  355.  
  356.     // Is the rect close enought to left or right edges
  357.     // of the magnetRect?
  358.     SnapRightToRight(snapRect, &monitorRect, snapToDistance);
  359.     SnapLeftToLeft(snapRect, &monitorRect, snapToDistance);
  360.  
  361.     // How about the top or bottom edges?
  362.     SnapBottomToBottom(snapRect, &monitorRect, snapToDistance);
  363.     SnapTopToTop(snapRect, &monitorRect, snapToDistance);
  364. } // END MonitorSnapProc
  365.  
  366. // ---------------------------------------------------------------------------
  367.  
  368. /*
  369.     WindowSnapProc.
  370.     Snap to edges of other windows.
  371. */
  372.  
  373. enum {
  374.     kMaxWindowsSnap = 10
  375. };
  376.  
  377. void WindowSnapProc(WindowPtr windowToDrag, short snapToDistance, Rect *snapRect) {
  378.     Rect strucRgnRect;
  379.     short snapRectWidth, snapRectHeight;
  380.     short i, numWindows;
  381.     WindowRef theWindow;
  382.     WindowRef windowList[kMaxWindowsSnap];
  383.     RgnHandle strucRgn;
  384.     
  385.     theWindow = FrontWindow();
  386.     if (theWindow == NULL) return;
  387.     
  388.     snapRectWidth = snapRect->right - snapRect->left;
  389.     snapRectHeight = snapRect->bottom - snapRect->top;
  390.  
  391.     // Make a list of windows which meet our criteria
  392.     // of "snappable" windows
  393.     i = numWindows = 0;
  394.     while ((theWindow != NULL) && (i < kMaxWindowsSnap)) {
  395.         if (IsWindowVisible(theWindow) && (theWindow != windowToDrag)) {
  396.             windowList[i++] = theWindow;
  397.             numWindows++;
  398.         }
  399.         theWindow = GetNextWindow(theWindow);
  400.     }
  401.  
  402.     strucRgn = NewRgn();
  403.  
  404.     // We're going to give the topmost window the highest priority
  405.     // when determining which window to snap to. To do this, we
  406.     // step through the window list backwards (since the topmost
  407.     // window is the first entry in the window list).
  408.     for (i = numWindows-1; i >= 0; i--) {
  409.         GetWindowStructureRgn(windowList[i], strucRgn);
  410.         strucRgnRect = (**strucRgn).rgnBBox;
  411.         
  412.         /*
  413.             Below is a tortuous road of if-then-checks. sigh.
  414.  
  415.             The problem here is that we have to do eight checks:
  416.             the left of the snap window to the left of the magnet window,
  417.             the left of the snap window to the right of the magnet window,
  418.             the top of the snap window to the top of the magnet window,
  419.             the top of the snap window to the bottom of the magnet window,
  420.             and so on for all 4 sides x 2...
  421.         */
  422.  
  423.         // Check left to left snapping
  424.         if (WithinRange(snapRect->left, strucRgnRect.left, snapToDistance)) {
  425.             if ((snapRect->top > strucRgnRect.bottom) ||
  426.                 (snapRect->bottom < strucRgnRect.top)) {
  427.                 // Do nothing
  428.             }
  429.             else {
  430.                 snapRect->left = strucRgnRect.left;
  431.                 snapRect->right = snapRect->left + snapRectWidth;
  432.             }
  433.             // Top gets preference, so we check that last
  434.             SnapBottomToBottom(snapRect, &strucRgnRect, snapToDistance);
  435.             SnapTopToTop(snapRect, &strucRgnRect, snapToDistance);
  436.         }
  437.         // Check right to right snapping
  438.         else if (WithinRange(snapRect->right, strucRgnRect.right, snapToDistance)) {
  439.             if ((snapRect->top > strucRgnRect.bottom) ||
  440.                 (snapRect->bottom < strucRgnRect.top)) {
  441.                 // Do nothing
  442.             }
  443.             else {
  444.                 snapRect->right = strucRgnRect.right;
  445.                 snapRect->left = snapRect->right - snapRectWidth;
  446.             }
  447.             // Top gets preference, so we check that last
  448.             SnapBottomToBottom(snapRect, &strucRgnRect, snapToDistance);
  449.             SnapTopToTop(snapRect, &strucRgnRect, snapToDistance);
  450.         }
  451.  
  452.  
  453.         // Check top to top snapping
  454.         if (WithinRange(snapRect->top, strucRgnRect.top, snapToDistance)) {
  455.             if ((snapRect->left > strucRgnRect.right) ||
  456.                 (snapRect->right < strucRgnRect.left)) {
  457.                 // Do nothing
  458.             }
  459.             else {
  460.                 snapRect->top = strucRgnRect.top;
  461.                 snapRect->bottom = snapRect->top + snapRectHeight;
  462.             }
  463.             // Left get preference, so check that last
  464.             SnapRightToRight(snapRect, &strucRgnRect, snapToDistance);
  465.             SnapLeftToLeft(snapRect, &strucRgnRect, snapToDistance);
  466.         }
  467.         // Check bottom to bottom snapping
  468.         else if (WithinRange(snapRect->bottom, strucRgnRect.bottom, snapToDistance)) {
  469.             if ((snapRect->left > strucRgnRect.right) ||
  470.                 (snapRect->right < strucRgnRect.left)) {
  471.                 // Do nothing
  472.             }
  473.             else {
  474.                 snapRect->bottom = strucRgnRect.bottom;
  475.                 snapRect->top = snapRect->bottom - snapRectHeight;
  476.             }
  477.             // Left get preference, so check that last
  478.             SnapRightToRight(snapRect, &strucRgnRect, snapToDistance);
  479.             SnapLeftToLeft(snapRect, &strucRgnRect, snapToDistance);
  480.         }
  481.  
  482.  
  483.         // Check left to right snapping
  484.         if (WithinRange(snapRect->left, strucRgnRect.right, snapToDistance)) {
  485.             if ((snapRect->top > strucRgnRect.bottom) ||
  486.                 (snapRect->bottom < strucRgnRect.top)) {
  487.                 // Do nothing
  488.             }
  489.             else {
  490.                 snapRect->left = strucRgnRect.right;
  491.                 snapRect->right = snapRect->left + snapRectWidth;
  492.                 
  493.             }
  494.             // Top gets preference, so we check that last
  495.             SnapBottomToBottom(snapRect, &strucRgnRect, snapToDistance);
  496.             SnapTopToTop(snapRect, &strucRgnRect, snapToDistance);
  497.         }
  498.         // Check right to left snapping
  499.         else if (WithinRange(snapRect->right, strucRgnRect.left, snapToDistance)) {
  500.             if ((snapRect->top > strucRgnRect.bottom) ||
  501.                 (snapRect->bottom < strucRgnRect.top)) {
  502.                 // Do nothing
  503.             }
  504.             else {
  505.                 snapRect->right = strucRgnRect.left;
  506.                 snapRect->left = snapRect->right - snapRectWidth;
  507.             }
  508.             // Top gets preference, so we check that last
  509.             SnapBottomToBottom(snapRect, &strucRgnRect, snapToDistance);
  510.             SnapTopToTop(snapRect, &strucRgnRect, snapToDistance);
  511.         }
  512.  
  513.  
  514.         // Check top to bottom snapping
  515.         if (WithinRange(snapRect->top, strucRgnRect.bottom, snapToDistance)) {
  516.             if ((snapRect->left > strucRgnRect.right) ||
  517.                 (snapRect->right < strucRgnRect.left)) {
  518.                 // Do nothing
  519.             }
  520.             else {
  521.                 snapRect->top = strucRgnRect.bottom;
  522.                 snapRect->bottom = snapRect->top + snapRectHeight;
  523.             }
  524.             // Left get preference, so check that last
  525.             SnapRightToRight(snapRect, &strucRgnRect, snapToDistance);
  526.             SnapLeftToLeft(snapRect, &strucRgnRect, snapToDistance);
  527.         }
  528.         // Check bottom to top snapping
  529.         else if (WithinRange(snapRect->bottom, strucRgnRect.top, snapToDistance)) {
  530.             if ((snapRect->left > strucRgnRect.right) ||
  531.                 (snapRect->right < strucRgnRect.left)) {
  532.                 // Do nothing
  533.             }
  534.             else {
  535.                 snapRect->bottom = strucRgnRect.top;
  536.                 snapRect->top = snapRect->bottom - snapRectHeight;
  537.             }
  538.             // Left get preference, so check that last
  539.             SnapRightToRight(snapRect, &strucRgnRect, snapToDistance);
  540.             SnapLeftToLeft(snapRect, &strucRgnRect, snapToDistance);
  541.         }
  542.     }
  543.     
  544.     DisposeRgn(strucRgn);
  545. } // END WindowSnapProc
  546.  
  547. // ---------------------------------------------------------------------------
  548.  
  549. /*
  550.     GridSnapProc.
  551.     Snap rect to the grid.
  552. */
  553.  
  554. void GridSnapProc(WindowPtr windowToDrag, short gridSize, Rect *snapRect) {
  555.     short leftMod, topMod;
  556.     short snapRectWidth, snapRectHeight;
  557.  
  558.     snapRectWidth = snapRect->right - snapRect->left;
  559.     snapRectHeight = snapRect->bottom - snapRect->top;
  560.     
  561.     leftMod = snapRect->left / gridSize;
  562.     snapRect->left = leftMod * gridSize;
  563.     topMod = snapRect->top / gridSize;
  564.     snapRect->top = topMod * gridSize;
  565.     snapRect->right = snapRect->left + snapRectWidth;
  566.     snapRect->bottom = snapRect->top + snapRectHeight;
  567. } // END GridSnapProc
  568.  
  569. // ---------------------------------------------------------------------------
  570.  
  571. void SnapBottomToBottom(Rect *snapRect, const Rect *magnetRect, short snapToDistance) {
  572.     short snapRectHeight;
  573.  
  574.     if (WithinRange(snapRect->bottom, magnetRect->bottom, snapToDistance)) {
  575.         snapRectHeight = snapRect->bottom - snapRect->top;
  576.         snapRect->bottom = magnetRect->bottom;
  577.         snapRect->top = snapRect->bottom - snapRectHeight;
  578.     }
  579. } // END SnapBottomToBottom
  580.  
  581. void SnapTopToTop(Rect *snapRect, const Rect *magnetRect, short snapToDistance) {
  582.     short snapRectHeight;
  583.  
  584.     if (WithinRange(snapRect->top, magnetRect->top, snapToDistance)) {
  585.         snapRectHeight = snapRect->bottom - snapRect->top;
  586.         snapRect->top = magnetRect->top;
  587.         snapRect->bottom = snapRect->top + snapRectHeight;
  588.     }
  589. } // END SnapTopToTop
  590.  
  591. void SnapRightToRight(Rect *snapRect, const Rect *magnetRect, short snapToDistance) {
  592.     short snapRectWidth;
  593.  
  594.     if (WithinRange(snapRect->right, magnetRect->right, snapToDistance)) {
  595.         snapRectWidth = snapRect->right - snapRect->left;
  596.         snapRect->right = magnetRect->right;
  597.         snapRect->left = snapRect->right - snapRectWidth;
  598.     }
  599. } // END SnapRightToRight
  600.  
  601. void SnapLeftToLeft(Rect *snapRect, const Rect *magnetRect, short snapToDistance) {
  602.     short snapRectWidth;
  603.  
  604.     if (WithinRange(snapRect->left, magnetRect->left, snapToDistance)) {
  605.         snapRectWidth = snapRect->right - snapRect->left;
  606.         snapRect->left = magnetRect->left;
  607.         snapRect->right = snapRect->left + snapRectWidth;
  608.     }
  609. } // END SnapLeftToLeft
  610.  
  611. // ---------------------------------------------------------------------------
  612.  
  613. Boolean WithinRange(short theValue, short rangeValue, short range) {
  614.     if (theValue < rangeValue + range &&
  615.         theValue > rangeValue - range)
  616.         return(true);
  617.     else
  618.         return(false);
  619. } // END WithinRange
  620.  
  621. // ---------------------------------------------------------------------------
  622.  
  623. void MoveRectTo(Rect *theRect, short h, short v) {
  624.     short width = theRect->right - theRect->left;
  625.     short height = theRect->bottom - theRect->top;
  626.     theRect->left = h;
  627.     theRect->top = v;
  628.     theRect->right = theRect->left + width;
  629.     theRect->bottom = theRect->top + height;
  630. } // END MoveRectTo
  631.  
  632. // ---------------------------------------------------------------------------
  633.  
  634. /*
  635.     Quick 'n dirty. Mea culpa...
  636. */
  637. Boolean IsKeyDown(unsigned short theKey);
  638. Boolean IsKeyDown(unsigned short theKey) {
  639.     unsigned char km[16];
  640.  
  641.     GetKeys(*((KeyMap*) &km));
  642.     return ((km[theKey>>3] >> (theKey & 7)) & 1);
  643. }
  644.  
  645. Boolean IsCmdKeyDown() {
  646.     return IsKeyDown(0x37);
  647. }
  648.  
  649. Boolean IsControlKeyDown() {
  650.     return IsKeyDown(0x3B);
  651. }
  652.  
  653. // ---------------------------------------------------------------------------
  654.  
  655. /*
  656.     The following routines were written by Norman Basham.
  657.     They were taken from "Monitors.cpp"
  658.     
  659.     Thanks a mil, Norman!
  660. */
  661. #pragma mark === Norman Basham ===
  662.  
  663. //    ------------------------------------------------------------------------
  664. //    Given rect r, which device does it overlap most.  An example of its use
  665. //    would be passing in (**wp->visRgn).rgnBBox to find out which device a
  666. //    window is overlapping the most, as in the case of zooming a window.
  667. //    ------------------------------------------------------------------------
  668. GDHandle GetDominantDevice (Rect *r)
  669. {
  670.     GDHandle            aGDevice;
  671.     GDHandle            bigGDevice;
  672.     Rect                screenRect;
  673.     Rect                sectRect;
  674.     long                area;
  675.     long                biggestArea = 0L;
  676.  
  677.     aGDevice = GetDeviceList ();                        //    start at begining of device list
  678.     while (aGDevice != nil)                                //    loop if device exists
  679.         {
  680.         if (IsActiveScreenDevice (aGDevice))            //    if device is a monitor and active
  681.             {
  682.             screenRect = (**aGDevice).gdRect;            //    get the devices global rect
  683.             SectRect (&screenRect, r, §Rect);        //    get overlapping rect of device and r
  684.             
  685.             area = GetRectArea (sectRect);                //    changed 3/21/94
  686.             if (area > biggestArea)                        //    if overlapping rect has the biggest area
  687.                 {
  688.                 bigGDevice = aGDevice;                    //    set big device to current device
  689.                 biggestArea = area;                        //    set big area to current area
  690.                 }
  691.     
  692.             aGDevice = GetNextDevice (aGDevice);        //    check next device in list
  693.             }
  694.         }
  695.         
  696.     return bigGDevice;                                    //    return device containing biggest portion of r
  697. }
  698.  
  699. //    ------------------------------------------------------------------------
  700. //    Given a device, return wether it is a monitor and wether it's active
  701. //    ------------------------------------------------------------------------
  702. Boolean IsActiveScreenDevice(GDHandle theDevice)
  703. {
  704.     return (
  705.             (TestDeviceAttribute (theDevice, screenDevice)) &&
  706.             (TestDeviceAttribute (theDevice, screenActive))
  707.             );
  708. }
  709.  
  710. long GetRectArea(Rect r)
  711. {
  712.     Rect        temp = r;
  713.  
  714.     OffsetRect (&temp, -temp.left, -temp.top);        //    rids us of neg values
  715.     return (long) temp.right * temp.bottom;            //    return width x heigth
  716. }
  717.